//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package roadmap_dao

import (
	"context"
	"strings"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/roadmap_content_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type EdgeKeyDbItem struct {
	FromNodeId   string `bson:"fromNodeId"`
	FromHandleId string `bson:"fromHandleId"`
	ToNodeId     string `bson:"toNodeId"`
	ToHandleId   string `bson:"toHandleId"`
}

type BasicEdgeInfoDbItem struct {
	EdgeType      uint32 `bson:"edgeType"`
	EdgeColor     string `bson:"edgeColor"`
	HasBeginArrow bool   `bson:"hasBeginArrow"`
	HasEndArrow   bool   `bson:"hasEndArrow"`
	EdgeWidth     uint32 `bson:"edgeWidth"`
}

type EdgeInfoDbItem struct {
	ItemId    string              `bson:"_id"` //自动生成
	RoadmapId string              `bson:"roadmapId"`
	EdgeKey   EdgeKeyDbItem       `bson:"edgeKey"`
	BasicInfo BasicEdgeInfoDbItem `bson:"basicInfo"`
}

func (item *EdgeInfoDbItem) ToEdgeInfo() *roadmap_content_api.EdgeInfo {
	return &roadmap_content_api.EdgeInfo{
		EdgeKey: &roadmap_content_api.EdgeKey{
			FromNodeId:   item.EdgeKey.FromNodeId,
			FromHandleId: item.EdgeKey.FromHandleId,
			ToNodeId:     item.EdgeKey.ToNodeId,
			ToHandleId:   item.EdgeKey.ToHandleId,
			RoadmapId:    item.RoadmapId,
		},
		BasicInfo: &roadmap_content_api.BasicEdgeInfo{
			EdgeType:      roadmap_content_api.EDGE_TYPE(item.BasicInfo.EdgeType),
			EdgeColor:     item.BasicInfo.EdgeColor,
			HasBeginArrow: item.BasicInfo.HasBeginArrow,
			HasEndArrow:   item.BasicInfo.HasEndArrow,
			EdgeWidth:     item.BasicInfo.EdgeWidth,
		},
	}
}

type _EdgeDao struct {
	mc *mongo.Collection
}

func newEdgeDao(db *mongo.Database) (*_EdgeDao, error) {
	mc := db.Collection(db_ns.DB_NS_ROADMAP + "edge")
	_, err := mc.Indexes().CreateOne(context.Background(), mongo.IndexModel{
		Keys: bson.D{{"roadmapId", 1}},
	})
	if err != nil {
		return nil, err
	}
	return &_EdgeDao{mc: mc}, nil
}

func (db *_EdgeDao) Upsert(ctx context.Context, dbItem *EdgeInfoDbItem) error {
	dbItem.ItemId = db.genItemId(dbItem.RoadmapId, &dbItem.EdgeKey)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": dbItem.ItemId}, bson.M{"$set": dbItem},
		options.Update().SetUpsert(true))
	return err
}

func (db *_EdgeDao) genItemId(roadmapId string, edgeKey *EdgeKeyDbItem) string {
	return strings.Join([]string{roadmapId,
		edgeKey.FromNodeId, edgeKey.FromHandleId,
		edgeKey.ToNodeId, edgeKey.ToHandleId}, ":")
}

func (db *_EdgeDao) Exist(ctx context.Context, roadmapId string, edgeKey *EdgeKeyDbItem) (bool, error) {
	itemId := db.genItemId(roadmapId, edgeKey)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId})
	return count > 0, err
}

func (db *_EdgeDao) Get(ctx context.Context, roadmapId string, edgeKey *EdgeKeyDbItem) (*EdgeInfoDbItem, error) {
	itemId := db.genItemId(roadmapId, edgeKey)
	retItem := &EdgeInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId}).Decode(retItem)
	return retItem, err
}

func (db *_EdgeDao) Remove(ctx context.Context, roadmapId string, edgeKey *EdgeKeyDbItem) error {
	itemId := db.genItemId(roadmapId, edgeKey)
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId})
	return err
}

func (db *_EdgeDao) RemoveByFromNode(ctx context.Context, roadmapId, nodeId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"roadmapId": roadmapId, "edgeKey.fromNodeId": nodeId})
	return err
}

func (db *_EdgeDao) RemoveByToNode(ctx context.Context, roadmapId, nodeId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"roadmapId": roadmapId, "edgeKey.toNodeId": nodeId})
	return err
}

func (db *_EdgeDao) RemoveByRoadmap(ctx context.Context, roadmapId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"roadmapId": roadmapId})
	return err
}

func (db *_EdgeDao) List(ctx context.Context, roadmapId string) ([]*EdgeInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"roadmapId": roadmapId})
	if err != nil {
		return nil, err
	}
	retList := []*EdgeInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_EdgeDao) UpdateBasicInfo(ctx context.Context, roadmapId string, edgeKey *EdgeKeyDbItem, basicDbItem *BasicEdgeInfoDbItem) error {
	itemId := db.genItemId(roadmapId, edgeKey)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo": basicDbItem}})
	return err
}
